﻿using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using TrackSystem.Contracts;
using TrackSystem.Entities.Attr;
using TrackSystem.Entities.Extensions;
using TrackSystem.Entities.Models;
using TrackSystem.Entities.RequestFeatures;
using TrackSystem.EntityFramework.Repositories.Extensions;
using TrackSystem.IO.Formula;

namespace TrackSystem.EntityFramework.Repositories
{
    public class VarRepository : Repositories<VarTable, int>, IVarRepository
    {
        private readonly IMapper mapper;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbContent"></param>
        /// <param name="mapper"></param>
        public VarRepository(TkDBContent dbContent, IMapper mapper) : base(dbContent)
        {
            this.mapper = mapper;
        }

        /// <summary>
        /// 更新传感器的变量表
        /// </summary>
        /// <param name="sensor"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> UpdateBaseSensor(SensorTable sensor)
        {
            try
            {
                // step 1  将当前数据库为该传感器的变量表都删除                
                var cur_vals = _dbContext.Vars.Where(a =>
                    a.snrloopId == sensor.Id &&
                    (a.Serial == VarSerialEnum.Sensor ||
                    a.Serial == VarSerialEnum.Attr_Sensor));
                foreach (var item in cur_vals)
                    _dbContext.Vars.Remove(item);
                var count = _dbContext.SaveChanges();

                // step 2 添加只读寄存器
                for (int i = 0; i < sensor.RO_Length; i++)
                {
                    VarTable varItem = new VarTable()
                    {
                        Name = $"{sensor.Name}_RO{sensor.RO_Address + i}",
                        Description = $"只读寄存器 {sensor.Name}",
                        Serial = VarSerialEnum.Sensor,
                        snrloopId = sensor.Id,
                        snrloopIndex = i,
                        ReadOnly = true,
                        DataType = DataTypeEnum.INTEGER,
                        ClacuateFormula = "",
                        IsRecord = false,
                        RecordCycle = 1000,
                        AlarmSetting = 0,
                        HighAlarmSet = 1000,
                        LowAlarmSet = 0
                    };
                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail("Same Name exist ({varItem.Name}");
                    _dbContext.Vars.Add(varItem);
                }

                // step 2 添加读写寄存器
                for (int i = 0; i < sensor.RW_Length; i++)
                {
                    VarTable varItem = new VarTable()
                    {
                        Name = $"{sensor.Name}_RW{sensor.RW_Address + i}",
                        Description = $"读写寄存器 {sensor.Name}",
                        Serial = VarSerialEnum.Sensor,
                        snrloopId = sensor.Id,
                        snrloopIndex = sensor.RO_Length + i,
                        ReadOnly = false,
                        DataType = DataTypeEnum.INTEGER,
                        ClacuateFormula = "",
                        IsRecord = false,
                        RecordCycle = 1000,
                        AlarmSetting = 0,
                        HighAlarmSet = 1000,
                        LowAlarmSet = 0
                    };
                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail("Same Name exist ({varItem.Name}");
                    _dbContext.Vars.Add(varItem);
                }

                // step 3 添加属性寄存器
                int idx = 0;
                foreach (var item in SensorAttrDefine.attrs)
                {
                    VarTable varItem = new VarTable
                    {
                        Name = $"{sensor.Name}_{item.Name}",
                        Serial = VarSerialEnum.Attr_Sensor,
                        snrloopId = sensor.Id,
                        snrloopIndex = item.Index,
                        Description = item.Desciption,
                        ReadOnly = item.ReadOnly,
                        DataType = item.DataType,
                        IsRecord = false,
                        RecordCycle = 1000,
                        AlarmSetting = 0,
                        HighAlarmSet = 1000,
                        LowAlarmSet = 0
                    };
                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail("Same Name exist ({varItem.Name}");
                    _dbContext.Vars.Add(varItem);
                    idx++;
                }

                count += await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Sensor {sensor.Name} variables updated successfully, {count} records added.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 删除传感器的所有变量
        /// </summary>
        /// <param name="sensorId"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> DeleteBaseSensor(int sensorId)
        {
            try
            {
                var cur_vals = _dbContext.Vars.Where(a =>
                    a.snrloopId == sensorId &&
                    (a.Serial == VarSerialEnum.Sensor ||
                    a.Serial == VarSerialEnum.Attr_Sensor));
                foreach (var item in cur_vals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Sensor {sensorId} variables deleted successfully, {count} records removed.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 更新通道的所有变量
        /// </summary>
        /// <param name="chn"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> UpdateBaseChannel(ChannelTable chn)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var cur_vals = _dbContext.Vars.Where(a => a.snrloopId == chn.Id && a.Serial == VarSerialEnum.Attr_Channel);
                foreach (var item in cur_vals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();

                // step 2  添加当前通道的所有属性变量
                int idx = 0;
                foreach (var item in ChannelAttrDefine.attrs)
                {
                    VarTable varItem = new VarTable
                    {
                        Name = $"{chn.Name}_{item.Name}",
                        Serial = VarSerialEnum.Attr_Channel,
                        snrloopId = chn.Id,
                        snrloopIndex = item.Index,
                        Description =item.Desciption,
                        ReadOnly = item.ReadOnly,
                        DataType = item.DataType,
                        IsRecord = false,
                        RecordCycle = 1000,
                        AlarmSetting = 0,
                        HighAlarmSet = 1000,
                        LowAlarmSet = 0
                    };

                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail($"Same Name exist {varItem.Name}");

                    _dbContext.Vars.Add(varItem);
                    idx++;
                }

                count += await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Channel {chn.Name} variables updated successfully, {count} records added.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 删除通道的所有变量
        /// </summary>
        /// <param name="chnId"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> DeleteBaseChannel(int chnId)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var cur_vals = _dbContext.Vars.Where(a => a.snrloopId == chnId && a.Serial == VarSerialEnum.Attr_Channel);
                foreach (var item in cur_vals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Channel {chnId} variables deleted successfully, {count} records removed.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 更新逻辑表达式相关的所有变量
        /// </summary>
        /// <param name="logic"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> UpdateBaseLogic(LogicTable logic)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var curVals = _dbContext.Vars.Where(a => a.snrloopId == logic.Id 
                                                          && a.Serial == VarSerialEnum.Attr_Logic);
                foreach (var item in curVals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();

                // step 2  添加当前通道的所有属性变量
                int idx = 0;
                foreach (var item in LogicAttr.attrs)
                {
                    var varItem = new VarTable
                    {
                        Name = $"{logic.Name}_{item.Name}",
                        Serial = VarSerialEnum.Attr_Logic,
                        snrloopId = logic.Id,
                        snrloopIndex = item.Index,
                        Description = item.Desciption,
                        ReadOnly = item.ReadOnly,
                        DataType = item.DataType,
                        IsRecord = false,
                        RecordCycle = 1000,
                        AlarmSetting = 0,
                        HighAlarmSet = 1000,
                        LowAlarmSet = 0
                    };

                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail($"Same Name exist {varItem.Name}");

                    _dbContext.Vars.Add(varItem);
                    idx++;
                }

                count += await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Logic {logic.Name} variables updated successfully, {count} records added.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 删除逻辑表达式相关的所有变量
        /// </summary>
        /// <param name="logicId"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> DeleteBaseLogic(int logicId)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var curVals = _dbContext.Vars.Where(a => a.snrloopId == logicId && 
                                                         a.Serial == VarSerialEnum.Attr_Logic);
                foreach (var item in curVals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Logic {logicId} variables deleted successfully, {count} records removed.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 删除回路相关变量
        /// </summary>
        /// <param name="loop">回路</param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> UpdateBaseLoop(LoopTable loop)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var curVals = _dbContext.Vars.Where(a => a.snrloopId == loop.Id
                                                          && a.Serial == VarSerialEnum.Attr_Loop);
                foreach (var item in curVals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();

                // step 2  添加当前通道的所有属性变量
                int idx = 0;
                foreach (var item in LoopVarDefine.attrs)
                {
                    var varItem = new VarTable
                    {
                        Name = $"{loop.Name}_{item.Name}",
                        Serial = VarSerialEnum.Attr_Loop,
                        snrloopId = loop.Id,
                        snrloopIndex = item.Index,
                        Description = item.Desciption,
                        ReadOnly = item.ReadOnly,
                        DataType = item.DataType,
                        IsRecord = false,
                        RecordCycle = 1000,
                        AlarmSetting = 0,
                        HighAlarmSet = 1000,
                        LowAlarmSet = 0
                    };

                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail($"Same Name exist {varItem.Name}");

                    _dbContext.Vars.Add(varItem);
                    idx++;
                }

                count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Loop {loop.Name} variables updated successfully, {count} records added.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 删除指定回路ID的所有变量
        /// </summary>
        /// <param name="loopId">回路ID号</param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> DeleteBasLoop(int loopId)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var curVals = _dbContext.Vars.Where(a => a.snrloopId == loopId &&
                                                         a.Serial == VarSerialEnum.Attr_Loop);
                foreach (var item in curVals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Loop {loopId} variables deleted successfully, {count} records removed.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }
        
        /// <summary>
        /// 更新服务器相关变量
        /// </summary>
        /// <param name="slave">回路</param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> UpdateBaseSlave(SlaveTable slave)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var curVals = _dbContext.Vars.Where(a => a.snrloopId == slave.Id
                                                          && a.Serial == VarSerialEnum.Attr_Slave);
                foreach (var item in curVals)
                    _dbContext.Vars.Remove(item);

                // step 2  添加当前通道的所有属性变量
                foreach (var varItem in SlaveAttr.attrs.Select(item => new VarTable
                         {
                             Name = $"{slave.Name}_{item.Name}",
                             Serial = VarSerialEnum.Attr_Slave,
                             snrloopId = slave.Id,
                             snrloopIndex = item.Index,
                             Description = item.Desciption,
                             ReadOnly = item.ReadOnly,
                             DataType = item.DataType,
                             IsRecord = false,
                             RecordCycle = 1000,
                             AlarmSetting = 0,
                             HighAlarmSet = 1000,
                             LowAlarmSet = 0
                         }))
                {
                    if (ExistName(varItem.Name))
                        return ApiResult<VarTable>.Fail($"Same Name exist {varItem.Name}");

                    _dbContext.Vars.Add(varItem);
                }

                var count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Slave {slave.Name} variables updated successfully, {count} records added.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }

        /// <summary>
        /// 删除指定服务器ID的所有变量
        /// </summary>
        /// <param name="slaveId">回路ID号</param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> DeleteBaseSlave(int slaveId)
        {
            try
            {
                // step 1  将当前数据库为该通道的变量表都删除
                var curVals = _dbContext.Vars.Where(a => a.snrloopId == slaveId &&
                                                         a.Serial == VarSerialEnum.Attr_Loop);
                foreach (var item in curVals)
                    _dbContext.Vars.Remove(item);
                var count = await _dbContext.SaveChangesAsync();
                return ApiResult<VarTable>.Ok($"Slave {slaveId} variables deleted successfully, {count} records removed.");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message);
            }
        }


        /// <summary>
        /// 当前所有变量表中是否存在该名称的变量表
        /// </summary>
        /// <param name="name">变量表名称</param>
        /// <returns></returns>
        public bool ExistName(string name)
        {
            return _dbContext.Vars.Where(a => a.Name == name).Count() > 0;
        }

        /// <summary>
        /// 添加变量表
        /// </summary>
        /// <param name="varTable">输入变量表</param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException">未实现异常</exception>
        public async Task<ApiResult<VarTable>> AddVarAsync(VarTable varTable)
        {
            try
            {
                if (varTable.Serial != VarSerialEnum.UserDefine)
                    return ApiResult<VarTable>.Fail("只能添加用户定义变量、速度变量和角度变量", StatusCodes.Status400BadRequest);

                if (ExistName(varTable.Name))
                    return ApiResult<VarTable>.Fail($"变量名称 {varTable.Name} 已存在", StatusCodes.Status400BadRequest);

                // 针对只读变量，需要校验输入表达式
                if (varTable.ReadOnly)
                {
                    var lstVarName = await _dbContext.Vars
                        .Select(a => new {a.Name, a.DataType})
                        .ToDictionaryAsync(a=>a.Name, b=>b.DataType);
                    if (!string.IsNullOrEmpty(varTable.ClacuateFormula))
                    {
                        var testResult = ClacuationFormula.TestFormula(varTable.ClacuateFormula, lstVarName);
                        if (!testResult.Success)
                        {
                            return ApiResult<VarTable>.Fail($"公式校验失败: {testResult.Message}", StatusCodes.Status400BadRequest);
                        }

                        var data = testResult.Data as OperandDef;
                        if (data.DataType != varTable.DataType ||
                            (data.DataType == DataTypeEnum.INTEGER && varTable.DataType == DataTypeEnum.DOUBLE))
                        {
                            return ApiResult<VarTable>.Fail($"公式校验失败: 返回类型为{data.DataType}, 应该为{varTable.DataType}", StatusCodes.Status400BadRequest);
                        }
                    }
                }
                else // 读写模式下，校验初始值设置是否合理
                {
                    if (varTable.DataType == DataTypeEnum.BOOLEAN && !bool.TryParse(varTable.InitValue, out _)) 
                    {
                        return ApiResult<VarTable>.Fail($"数据类型为布尔型，初始值设置不正确({varTable.InitValue})");
                    }
                    if (varTable.DataType == DataTypeEnum.INTEGER && !int.TryParse(varTable.InitValue, out _))
                    {
                        return ApiResult<VarTable>.Fail($"数据类型为整型，初始值设置不正确({varTable.InitValue})");
                    }
                    if (varTable.DataType == DataTypeEnum.DOUBLE && !double.TryParse(varTable.InitValue, out _))
                    {
                        return ApiResult<VarTable>.Fail($"数据类型为浮点型，初始值设置不正确({varTable.InitValue})");
                    }
                }

                var result = await InsertAsync(varTable);
                return ApiResult<VarTable>.Ok(result, "变量添加成功");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message, StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// 更新单个变量
        /// </summary>
        /// <param name="varTable"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> UpdateVarAsync(VarTable varTable)
        {
            try
            {
                if (varTable == null)
                    return ApiResult<VarTable>.Fail("变量不能为空", StatusCodes.Status400BadRequest);

                var count = await _dbContext.Vars.CountAsync(a => a.Id != varTable.Id && a.Name == varTable.Name);
                if (count > 0)
                    return ApiResult<VarTable>.Fail($"变量名称 {varTable.Name} 已存在", StatusCodes.Status400BadRequest);

                var source = await _dbContext.Vars.AsNoTracking<VarTable>().FirstOrDefaultAsync(a => a.Id == varTable.Id);
                if (source == null)
                {
                    return ApiResult<VarTable>.Fail("变量不存在", StatusCodes.Status400BadRequest);
                }

                if (varTable.Serial != VarSerialEnum.UserDefine )
                {
                    varTable.Serial = source.Serial; 
                }
                varTable.snrloopId = source.snrloopId;
                varTable.snrloopIndex = source.snrloopIndex;

                // 针对只读变量，需要校验输入表达式
                if (varTable.Serial == VarSerialEnum.UserDefine)
                {
                    if (varTable.ReadOnly)
                    {
                        var lstVarName = await _dbContext.Vars
                            .Select(a => new { a.Name, a.DataType })
                            .ToDictionaryAsync(a => a.Name, b => b.DataType);

                        if (!string.IsNullOrEmpty(varTable.ClacuateFormula))
                        {
                            var testResult = ClacuationFormula.TestFormula(varTable.ClacuateFormula, lstVarName);
                            if (!testResult.Success)
                            {
                                return ApiResult<VarTable>.Fail($"公式校验失败: {testResult.Message}", StatusCodes.Status400BadRequest);
                            }

                            var data = testResult.Data as OperandDef;
                            if (!(data.DataType == varTable.DataType ||
                                 (data.DataType == DataTypeEnum.INTEGER &&
                                  varTable.DataType == DataTypeEnum.DOUBLE))  )
                            {
                                return ApiResult<VarTable>.Fail($"公式校验失败: 返回类型为{data.DataType}, 应该为{varTable.DataType}", StatusCodes.Status400BadRequest);
                            }
                        }
                    }
                    else // 读写模式下，校验初始值设置是否合理
                    {
                        if (varTable.DataType == DataTypeEnum.BOOLEAN && !bool.TryParse(varTable.InitValue, out _))
                        {
                            return ApiResult<VarTable>.Fail($"数据类型为布尔型，初始值设置不正确({varTable.InitValue})");
                        }
                        if (varTable.DataType == DataTypeEnum.INTEGER && !int.TryParse(varTable.InitValue, out _))
                        {
                            return ApiResult<VarTable>.Fail($"数据类型为整型，初始值设置不正确({varTable.InitValue})");
                        }
                        if (varTable.DataType == DataTypeEnum.DOUBLE && !double.TryParse(varTable.InitValue, out _))
                        {
                            return ApiResult<VarTable>.Fail($"数据类型为浮点型，初始值设置不正确({varTable.InitValue})");
                        }
                    }
                }
                else
                { 
                    varTable.ReadOnly = source.ReadOnly;
                }


                var result = await UpdateAsync(varTable);
                return ApiResult<VarTable>.Ok(result, "变量修改成功");
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message, StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// 删除单个变量
        /// </summary>
        /// <param name="varId"></param>
        /// <returns></returns>
        public async Task<ApiResult<VarTable>> DeleteVarAsync(int varId)
        {
            try
            {
                var varTable = await _dbContext.Vars.FindAsync(varId);

                if (varTable == null)
                    return ApiResult<VarTable>.Fail("变量不存在", StatusCodes.Status400BadRequest);

                if (varTable.Serial == VarSerialEnum.UserDefine)
                { 
                    await DeleteAsync(varTable);
                    return ApiResult<VarTable>.Ok(varTable, "变量删除成功");
                }

                return ApiResult<VarTable>.Fail("只能删除用户定义变量、速度变量、角度及滤波变量", StatusCodes.Status400BadRequest);
            }
            catch (Exception ex)
            {
                return ApiResult<VarTable>.Fail(ex.Message, StatusCodes.Status500InternalServerError);
            }
        }

        /// <summary>
        /// 获取所有的变量及数据类型
        /// </summary>
        /// <returns></returns>
        public async Task<Dictionary<string, DataTypeEnum>> GetNameTypeListAsync()
        {
            return await _dbContext.Vars
                .Select(a => new { a.Name, a.DataType })
                .ToDictionaryAsync(a => a.Name, b => b.DataType);
        }

        /// <summary>
        /// 获取分页变量表
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public  PagedList<VarTable> GetPagedVars(RequestParameter parameter)
        {
            return  Table.OrderByQuery(parameter.QueryString)
                .ToPagedList(parameter.PageNumber, parameter.PageSize);
        }

        public async Task<List<VarTable>> GetSensorRegsAsync(int id)
        {
            return  await Table.Where(a => a.snrloopId == id && a.Serial == VarSerialEnum.Sensor)
                .OrderBy(a => a.snrloopIndex)
                .ToListAsync();
        }
    }
}
     